[NET] back: Loopback must copy foreign mappings.
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Fri, 29 Sep 2006 18:12:15 +0000 (19:12 +0100)
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Fri, 29 Sep 2006 18:12:15 +0000 (19:12 +0100)
Signed-off-by: Keir Fraser <keir@xensource.com>
linux-2.6-xen-sparse/drivers/xen/netback/loopback.c
linux-2.6-xen-sparse/drivers/xen/netback/netback.c

index 53392a42a385f87adf6b5f62f253e4ef45bfdbac..d021c9689a4b55a15eb639bb89a2551d8f51caa6 100644 (file)
@@ -79,10 +79,60 @@ static int loopback_close(struct net_device *dev)
        return 0;
 }
 
+#ifdef CONFIG_X86
+static int is_foreign(unsigned long pfn)
+{
+       /* NB. Play it safe for auto-translation mode. */
+       return (xen_feature(XENFEAT_auto_translated_physmap) ||
+               (phys_to_machine_mapping[pfn] & FOREIGN_FRAME_BIT));
+}
+#else
+/* How to detect a foreign mapping? Play it safe. */
+#define is_foreign(pfn)        (1)
+#endif
+
+static int skb_remove_foreign_references(struct sk_buff *skb)
+{
+       struct page *page;
+       unsigned long pfn;
+       int i, off;
+       char *vaddr;
+
+       BUG_ON(skb_shinfo(skb)->frag_list);
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               pfn = page_to_pfn(skb_shinfo(skb)->frags[i].page);
+               if (!is_foreign(pfn))
+                       continue;
+               
+               page = alloc_page(GFP_ATOMIC | __GFP_NOWARN);
+               if (unlikely(!page))
+                       return 0;
+
+               vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);
+               off = skb_shinfo(skb)->frags[i].page_offset;
+               memcpy(page_address(page) + off,
+                      vaddr + off,
+                      skb_shinfo(skb)->frags[i].size);
+               kunmap_skb_frag(vaddr);
+
+               put_page(skb_shinfo(skb)->frags[i].page);
+               skb_shinfo(skb)->frags[i].page = page;
+       }
+
+       return 1;
+}
+
 static int loopback_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct net_private *np = netdev_priv(dev);
 
+       if (!skb_remove_foreign_references(skb)) {
+               np->stats.tx_dropped++;
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
        dst_release(skb->dst);
        skb->dst = NULL;
 
index ad8236c82fc7e4ef72df8906f3faf2dbb2d5bd26..2da471c195ec898980d50aa7c68affbe5400b595 100644 (file)
@@ -217,7 +217,7 @@ static struct sk_buff *netbk_copy_skb(struct sk_buff *skb)
                copy = len >= PAGE_SIZE ? PAGE_SIZE : len;
                zero = len >= PAGE_SIZE ? 0 : __GFP_ZERO;
 
-               page = alloc_page(GFP_ATOMIC | zero);
+               page = alloc_page(GFP_ATOMIC | __GFP_NOWARN | zero);
                if (unlikely(!page))
                        goto err_free;